package com.yunpeng.aidevice.dao.service.impl;

import com.yunpeng.aidevice.common.constant.ConfigConstantValues;
import com.yunpeng.aidevice.common.constant.ConstantValues;
import com.yunpeng.aidevice.common.dto.save.OrgSaveDTO;
import com.yunpeng.aidevice.common.enums.ResultCodeEnum;
import com.yunpeng.aidevice.dao.constant.DictionariesConstant;
import com.yunpeng.aidevice.dao.mapper.entity.OrganizationMapper;
import com.yunpeng.aidevice.dao.mapper.relation.DeviceRelationOrganizationMapper;
import com.yunpeng.aidevice.dao.mapper.relation.OrganizationPermissionMapper;
import com.yunpeng.aidevice.dao.model.entity.MerchantDO;
import com.yunpeng.aidevice.dao.model.entity.OrganizationDO;
import com.yunpeng.aidevice.dao.model.relation.DeviceRelationOrganizationDO;
import com.yunpeng.aidevice.dao.service.ICodeGenerateCommonService;
import com.yunpeng.aidevice.dao.service.IOrganizationService;
import com.yunpeng.aidevice.dao.util.RelationUpdateUtil;
import com.yunpeng.cache.util.DistributedLockHelper;
import com.yunpeng.common.exception.BizException;
import org.apache.commons.lang3.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Isolation;
import org.springframework.transaction.annotation.Propagation;
import org.springframework.transaction.annotation.Transactional;

import javax.annotation.Resource;
import java.time.LocalDateTime;
import java.util.*;


/**
 * OrganizationServiceImpl 组织机构内部服务
 *
 * @author : Moore(hemw)
 * @date : 2020-04-08
 */
@Service
public class OrganizationServiceImpl implements IOrganizationService {

    /**
     * 日志对象
     */
    private static final Logger LOGGER = LoggerFactory.getLogger(OrganizationServiceImpl.class);

    /**
     * 当前服务的实体表名
     */
    private static final String TABLE_NAME = "t_ai_device_organization";

    @Resource
    private ICodeGenerateCommonService codeGenerateCommonService;

    @Resource
    private OrganizationMapper organizationMapper;

    @Resource
    private OrganizationPermissionMapper organizationPermissionMapper;

    @Resource
    private DeviceRelationOrganizationMapper deviceRelationOrganizationMapper;

    //region 对外服务

    @Override
    /**
     * 获取顶级节点
     *
     * @param rootPath     节点路径
     * @param merchantCode 商户代码
     * @param tenantCode   租户代码
     * @return
     */
    public OrganizationDO getRoot(String rootPath, String merchantCode, String tenantCode) {
        if (StringUtils.isBlank(rootPath)) {
            return null;
        }
        String[] pathLocations = rootPath.trim().split(ConstantValues.ORGANIZATION_NODE_PATH_SPLIT);
        return getNode(pathLocations[0], true, merchantCode, tenantCode);
    }

    @Override
    /**
     * 获取节点
     *
     * @param nodePath     节点路径
     * @param merchantCode 商户代码
     * @param tenantCode   租户代码
     * @return
     */
    public OrganizationDO getNode(String nodePath, String merchantCode, String tenantCode) {
        if (StringUtils.isBlank(nodePath)) {
            return null;
        }
        return getNode(nodePath, false, merchantCode, tenantCode);
    }

    @Override
    /**
     * 获取节点
     *
     * @param nodeCode     节点代码
     * @param merchantCode 商户代码
     * @param tenantCode   租户代码
     * @return
     */
    public OrganizationDO getByCode(String nodeCode, String merchantCode, String tenantCode) {
        if (StringUtils.isBlank(nodeCode)) {
            throw new BizException(ResultCodeEnum.PARAMETER_CHECK_FAILED, "nodeCode不能为空");
        }
        if (StringUtils.isBlank(merchantCode)) {
            throw new BizException(ResultCodeEnum.PARAMETER_CHECK_FAILED, "merchantCode不能为空");
        }
        if (StringUtils.isBlank(tenantCode)) {
            throw new BizException(ResultCodeEnum.PARAMETER_CHECK_FAILED, "tenantCode不能为空");
        }
        OrganizationDO organizationDO = organizationMapper.getByCode(nodeCode);
        if (organizationDO == null) {
            return null;
        }
        if (merchantCode.equals(organizationDO.getMerchantCode()) && tenantCode.equals(organizationDO.getTenantCode())) {
            return organizationDO;
        }
        return null;
    }

    @Override
    /**
     * 新增节点
     *
     * @param orgSaveDTO           节点保存信息
     * @param operatorCode         操作人代码
     * @param parentNode           父节点
     * @return
     */
    public String addNewNode(OrgSaveDTO orgSaveDTO, String operatorCode, OrganizationDO parentNode) {
        if (organizationMapper.countNumberExists(orgSaveDTO.getNumber(), orgSaveDTO.getMerchantCode(), DictionariesConstant.INSTANCE.getDefaultTenantCode()) > 0) {
            LOGGER.error("save organization number exists error : {}", orgSaveDTO);
            throw new BizException(ResultCodeEnum.EXISTS_NUMBER_FAILED);
        }
        OrganizationDO organizationDO = new OrganizationDO();
        organizationDO.initForNew(operatorCode);
        if (parentNode != null && parentNode.getId() != null) {
            organizationDO.setParentCode(parentNode.getCode());
            organizationDO.setLevel(parentNode.getLevel() + 1);
            String nodePath = String.format("%s%s%s", parentNode.getNodePath(), ConstantValues.ORGANIZATION_NODE_PATH_SPLIT, orgSaveDTO.getPath());
            organizationDO.setNodePath(nodePath);
        } else {
            organizationDO.setParentCode(null);
            organizationDO.setLevel(0);
            organizationDO.setNodePath(orgSaveDTO.getPath());
        }
        organizationDO.setName(orgSaveDTO.getName());
        organizationDO.setPathName(orgSaveDTO.getSimpleName());
        organizationDO.setMerchantCode(orgSaveDTO.getMerchantCode());
        organizationDO.setNumber(orgSaveDTO.getNumber());
        organizationDO.setDisabled(orgSaveDTO.getDisabled());
        if (DistributedLockHelper.INSTANCE.getLock(TABLE_NAME, ConfigConstantValues.APPLICATION_NAME)) {
            organizationDO.setCode(codeGenerateCommonService.generateCode(TABLE_NAME));
            try {
                organizationMapper.add(organizationDO);
                return organizationDO.getCode();
            } catch (Exception ex) {
                LOGGER.error("save organization node error : {}", organizationDO, ex);
                throw new BizException(ResultCodeEnum.SAVE_DATA_FAILED);
            } finally {
                codeGenerateCommonService.holdCountMinus(TABLE_NAME);
                DistributedLockHelper.INSTANCE.unlock(TABLE_NAME, ConfigConstantValues.APPLICATION_NAME);
            }
        } else {
            return null;
        }
    }

    @Override
    /**
     * 根据节点路径左匹配，查询出所有下级节点和其本身
     *
     * @param nodePath     节点路径
     * @param containSelf  是否包含本身
     * @param merchantCode 商户代码
     * @param tenantCode   租户代码
     * @return
     */
    public List<OrganizationDO> queryNodeListByPath(String nodePath, Boolean containSelf, String merchantCode, String tenantCode) {
        if (StringUtils.isBlank(nodePath)) {
            return null;
        }
        return organizationMapper.queryListByPath(nodePath, containSelf, merchantCode, tenantCode);
    }

    @Override
    /**
     * 删除节点
     *
     * @param nodeCode
     * @return
     */
    public Boolean removeOrganizationNode(String nodeCode) {
        if (StringUtils.isBlank(nodeCode)) {
            return false;
        }
        try {
            doDeleteOrganization(nodeCode);
            return true;
        } catch (Exception ex) {
            LOGGER.error("transaction delete data error,nodeCode {}.", nodeCode, ex);
            return false;
        }
    }

    @Override
    /**
     * 统计权限关联组织机构数量
     *
     * @param permissionCode 二选一
     * @param orgCode        二选一
     * @param now            当前时间
     * @return
     */
    public Integer countPermissionRelation(String permissionCode, String orgCode, Date now) {
        if (StringUtils.isBlank(orgCode) && StringUtils.isBlank(permissionCode)) {
            LOGGER.error("countPermissionRelation error,could all null .");
            throw new BizException(ResultCodeEnum.PARAMETER_CHECK_FAILED, "必须有一个不为空");
        }
        return organizationPermissionMapper.existsCount(permissionCode, orgCode, now);
    }

    @Override
    /**
     * 更新组织机构信息
     *
     * @param orgSaveDTO   组织机构信息
     * @param operatorCode 操作人代码
     * @param parentNode   父节点
     * @return
     */
    public Boolean updateOrganization(OrgSaveDTO orgSaveDTO, String operatorCode, OrganizationDO parentNode) {
        if (orgSaveDTO == null || StringUtils.isBlank(orgSaveDTO.getCode())) {
            LOGGER.error("updateOrganization error,parameter is null or invalid code .");
            return false;
        }
        OrganizationDO organizationDO = getByCode(orgSaveDTO.getCode(), orgSaveDTO.getMerchantCode(), DictionariesConstant.INSTANCE.getDefaultTenantCode());
        if (organizationDO == null) {
            LOGGER.error("updateOrganization error,org not exists .");
            throw new BizException(ResultCodeEnum.NOT_EXISTS_DATA_FAILED, "无此组织");
        }
        if (!organizationDO.getMerchantCode().equals(orgSaveDTO.getMerchantCode()) || !organizationDO.getTenantCode().equals(DictionariesConstant.INSTANCE.getDefaultTenantCode())) {
            LOGGER.error("updateOrganization error,has not permission.");
            throw new BizException(ResultCodeEnum.HAS_NOT_PERMISSION);
        }
        boolean redundantUpdated = false;
        Map<String, Object> updateMap = new HashMap<>(16);
        String originPath = organizationDO.getNodePath();
        String newPath = originPath;
        String[] pathValues = originPath.split(ConstantValues.ORGANIZATION_NODE_PATH_SPLIT);
        //父节点处理
        if (parentNode != null && StringUtils.isNotBlank(parentNode.getCode())) {
            if (!orgSaveDTO.getParentCode().equals(parentNode.getCode())) {
                newPath = String.format("%s%s%s", parentNode.getNodePath(), ConstantValues.ORGANIZATION_NODE_PATH_SPLIT, pathValues[pathValues.length - 1]);
                updateMap.put("level", parentNode.getLevel() + 1);
                updateMap.put("node_path", newPath);
                updateMap.put("parent_code", parentNode.getCode());
                redundantUpdated = true;
            }
        }
        //名称处理
        if (StringUtils.isNotBlank(orgSaveDTO.getName()) && !orgSaveDTO.getName().equals(organizationDO.getName())) {
            updateMap.put("name", orgSaveDTO.getName());
            redundantUpdated = true;
        }
        //简称
        if (StringUtils.isNotBlank(orgSaveDTO.getSimpleName()) && !orgSaveDTO.getName().equals(organizationDO.getPathName())) {
            updateMap.put("path_name", orgSaveDTO.getSimpleName());
        }
        //编号
        if (StringUtils.isNotBlank(orgSaveDTO.getNumber())) {
            if (organizationMapper.countNumberExistsExcludeCode(orgSaveDTO.getNumber(), orgSaveDTO.getCode(), orgSaveDTO.getMerchantCode(), DictionariesConstant.INSTANCE.getDefaultTenantCode()) > 0) {
                LOGGER.error("updateOrganization error,number exists .");
                throw new BizException(ResultCodeEnum.EXISTS_NUMBER_FAILED);
            }
            updateMap.put("number", orgSaveDTO.getNumber());
        }
        updateMap.put("updater", operatorCode);
        updateMap.put("update_time", LocalDateTime.now());
        //执行更新
        try {
            doUpdatePath(newPath, originPath, organizationDO.getId(), updateMap);
            if (redundantUpdated) {
                RelationUpdateUtil.INSTANCE.orgInfoUpdatePush(orgSaveDTO.getName(), newPath, organizationDO.getCode());
            }
            return true;
        } catch (Exception ex) {
            LOGGER.error("updateOrganization error.", ex);
            return false;
        }
    }

    @Override
    /**
     * 禁用组织
     *
     * @param operatorCode 操作人代码
     * @param merchantCode 商户代码
     * @param tenantCode   租户代码
     * @param nodeCode     节点代码
     * @return
     */
    public Boolean disable(String operatorCode, String merchantCode, String tenantCode, String nodeCode) {
        OrganizationDO organizationDO = getByCode(nodeCode, merchantCode, DictionariesConstant.INSTANCE.getDefaultTenantCode());
        if (organizationDO == null) {
            LOGGER.error("disable error,org not exists .");
            throw new BizException(ResultCodeEnum.NOT_EXISTS_DATA_FAILED, "无此组织");
        }
        if (!organizationDO.getMerchantCode().equals(merchantCode) || !organizationDO.getTenantCode().equals(tenantCode)) {
            LOGGER.error("disable error,has not permission.");
            throw new BizException(ResultCodeEnum.HAS_NOT_PERMISSION);
        }
        Map<String, Object> updateMap = new HashMap<>(16);
        updateMap.put("updater", operatorCode);
        updateMap.put("update_time", LocalDateTime.now());
        updateMap.put("disabled", true);
        //事务处理
        try {
            doDisableEnable(true, merchantCode, tenantCode, organizationDO.getNodePath(), organizationDO.getId(), updateMap);
            return true;
        } catch (Exception ex) {
            LOGGER.error("disable error.", ex);
            return false;
        }
    }

    @Override
    /**
     * 启用组织
     *
     * @param operatorCode 操作人代码
     * @param merchantCode 商户代码
     * @param tenantCode   租户代码
     * @param nodeCode     节点代码
     * @return
     */
    public Boolean enable(String operatorCode, String merchantCode, String tenantCode, String nodeCode) {
        OrganizationDO organizationDO = getByCode(nodeCode, merchantCode, DictionariesConstant.INSTANCE.getDefaultTenantCode());
        if (organizationDO == null) {
            LOGGER.error("disable error,org not exists .");
            throw new BizException(ResultCodeEnum.NOT_EXISTS_DATA_FAILED, "无此组织");
        }
        if (!organizationDO.getMerchantCode().equals(merchantCode) || !organizationDO.getTenantCode().equals(tenantCode)) {
            LOGGER.error("disable error,has not permission.");
            throw new BizException(ResultCodeEnum.HAS_NOT_PERMISSION);
        }
        Map<String, Object> updateMap = new HashMap<>(16);
        updateMap.put("updater", operatorCode);
        updateMap.put("update_time", LocalDateTime.now());
        updateMap.put("disabled", false);
        organizationMapper.update(organizationDO.getId(), updateMap);
        //事务处理
        try {
            doDisableEnable(false, merchantCode, tenantCode, organizationDO.getNodePath(), organizationDO.getId(), updateMap);
            return true;
        } catch (Exception ex) {
            LOGGER.error("disable error.", ex);
            return false;
        }
    }

    @Override
    /**
     * 根据组织编号获取组织
     *
     * @param number       组织编号
     * @param merchantCode 商户代码
     * @param tenantCode   租户代码
     * @return
     */
    public OrganizationDO getByNumber(String number, String merchantCode, String tenantCode) {
        if (StringUtils.isBlank(number) || StringUtils.isBlank(merchantCode) || StringUtils.isBlank(tenantCode)) {
            return null;
        }
        return organizationMapper.getByNumber(merchantCode, tenantCode, number);
    }

    @Override
    /**
     * 获取设备关联的组织代码
     *
     * @param deviceCode 设备代码
     * @return
     */
    public List<String> getRelationOrgCodesByDevice(String deviceCode) {
        if (StringUtils.isBlank(deviceCode)) {
            return new ArrayList<>();
        }
        Map<String, Object> queryMap = new HashMap<>(16);
        queryMap.put("flag", 1);
        queryMap.put("device_code", deviceCode);
        List<DeviceRelationOrganizationDO> deviceRelationOrganizationDOList = deviceRelationOrganizationMapper.queryForList(queryMap);
        List<String> orgCodes = new ArrayList<>();
        if (deviceRelationOrganizationDOList != null) {
            for (DeviceRelationOrganizationDO deviceRelationOrganizationDO : deviceRelationOrganizationDOList) {
                orgCodes.add(deviceRelationOrganizationDO.getOrganizationCode());
            }
        }
        return orgCodes;
    }

    @Override
    /**
     * 获取所有商户下的组织部门（最大2000）
     *
     * @param merchantCode
     * @param tenantCode
     * @return
     */
    public List<OrganizationDO> getAllMerchantOrgList(String merchantCode, String tenantCode) {
        if (StringUtils.isBlank(merchantCode)) {
            return new ArrayList<>();
        }
        if (StringUtils.isBlank(tenantCode)) {
            return new ArrayList<>();
        }
        Map<String, Object> queryMap = new HashMap<>(16);
        queryMap.put("flag", 1);
        queryMap.put("merchant_code", merchantCode);
        queryMap.put("tenant_code", tenantCode);
        return organizationMapper.queryForList(queryMap);
    }

    @Override
    /**
     * 创建商户默认组织
     *
     * @param merchantDO
     * @param operatorCode
     * @return
     */
    public String addMerchantDefaultOrg(MerchantDO merchantDO, String operatorCode) {
        OrgSaveDTO orgSaveDTO = new OrgSaveDTO();
        orgSaveDTO.setName(merchantDO.getName());
        orgSaveDTO.setPath(merchantDO.getNumber());
        orgSaveDTO.setSimpleName(merchantDO.getName());
        orgSaveDTO.setDisabled(false);
        orgSaveDTO.setMerchantCode(merchantDO.getCode());
        orgSaveDTO.setNumber("001");
        try {
            return addNewNode(orgSaveDTO, operatorCode, null);
        } catch (Exception ex) {
            LOGGER.error("add merchant default org error ,ex", ex);
        }
        return null;
    }

    //endregion


    //region 私有方法

    @Transactional(propagation = Propagation.REQUIRED, isolation = Isolation.DEFAULT, timeout = 3600, rollbackFor = Exception.class)
    void doDeleteOrganization(String nodeCode) {
        organizationMapper.deleteByCode(nodeCode);
        organizationPermissionMapper.deleteByOrganizationCode(nodeCode);
        deviceRelationOrganizationMapper.deleteByOrganizationCode(nodeCode);
    }

    @Transactional(propagation = Propagation.REQUIRED, isolation = Isolation.DEFAULT, timeout = 3600, rollbackFor = Exception.class)
    void doUpdatePath(String newPath, String originPath, Long organizationId, Map<String, Object> updateMap) {
        if (!newPath.equals(originPath)) {
            organizationMapper.updatePath(newPath, originPath);
        }
        organizationMapper.update(organizationId, updateMap);
    }

    @Transactional(propagation = Propagation.REQUIRED, isolation = Isolation.DEFAULT, timeout = 3600, rollbackFor = Exception.class)
    void doDisableEnable(Boolean disabled, String merchantCode, String tenantCode, String nodePath, Long organizationId, Map<String, Object> updateMap) {
        organizationMapper.update(organizationId, updateMap);
        organizationMapper.disableEnableChild(disabled, merchantCode, tenantCode, nodePath);
    }

    /**
     * 获取节点
     *
     * @param rootPath 节点全路径
     * @param isRoot   节点是否跟节点
     * @return
     */
    private OrganizationDO getNode(String rootPath, Boolean isRoot, String merchantCode, String tenantCode) {
        return organizationMapper.getByPath(rootPath, isRoot, merchantCode, tenantCode);
    }

    //endregion
}
